#ifndef _STRAWGUI_NAMED_OBJECT_CONTAINER_H_
#define _STRAWGUI_NAMED_OBJECT_CONTAINER_H_

#include "sNamedObject.h"
#include "sObserver.h"
#include "sNamedObjectFabric.h"

namespace sgui
{

#define CONTAINER_WRAPPER(ct) sNamedObjectContainer< ct >

template < class T >
class sNamedObjectContainer : public obs::sObserver, public sGUIObject
{
public:
    typedef typename std::map<std::wstring, T*>::iterator iterator;
    typedef typename std::map<std::wstring, T*>::const_iterator const_iterator;

    typedef typename std::map<std::wstring, T*>::reverse_iterator reverse_iterator;
    typedef typename std::map<std::wstring, T*>::const_reverse_iterator const_reverse_iterator;
private:
    std::map<std::wstring, T* > m_items;
private:
    void setName(const std::wstring& oldName, const std::wstring& newName)
    {
        T* item = (*this)[oldName];
        if (item)
        {
            remove(oldName, false);
            add(newName);
        }
    }

public:
	sNamedObjectContainer(sGUI& gui) :
	  sGUIObject(gui)
	{
	}

	T* operator[](const std::wstring& name) 
    {
        std::map<std::wstring, T* >::iterator it = m_items.find(name);  
        if (it != m_items.end())
        {
            return it->second;
        }
        return NULL;
    }

	const T* operator[](const std::wstring& name) const
    {
        std::map<std::wstring, T* >::const_iterator it = m_items.find(name);  
        if (it != m_items.end())
        {
            return it->second;
        }
        return NULL;
    }

    iterator begin()
    {
        return m_items.begin();
    }

    iterator end()
    {
        return m_items.end();
    }

    const_iterator begin() const
    {
        return m_items.begin();
    }

    const_iterator end() const
    {
        return m_items.end();
    }

    reverse_iterator rbegin()
    {
        return m_items.rbegin();
    }

    reverse_iterator rend()
    {
        return m_items.rend();
    }

    const_reverse_iterator rbegin() const
    {
        return m_items.rbegin();
    }

    const_reverse_iterator rend() const
    {
        return m_items.rend();
    }

    unsigned int size() const
    {
        return m_items.size();
    }

    void save(db::Record& file) const
    {
        file.setSize(size());
        std::map<std::wstring, T* >::const_iterator it;
        for (it = m_items.begin(); it != m_items.end(); ++it)
        {
            it->second->save(file[it->first]);
        }
    }

	
    void load(const db::Record& file, const fabric::sNamedObjectFabric& fabric)
    {
        clear();
        if (file.getClass() == db::RC_NAMEDCHILD)
        {
            for (db::Record::const_iterator i = file.childBegin(); i != file.childEnd(); i++)
            {
				T* obj = (T*)fabric.CreateObject(getGui(), *i->second);
				
				if (obj)
				{
					m_items[obj->getName()] = obj;
					obj->registerObserver(this);
				}
            }
        }
    }

    T* add(T* item)
    {
		if (item)
		{
			const std::wstring& name = item->getName();

			std::map<std::wstring, T* >::const_iterator it = m_items.find(name);  
			if (it != m_items.end())
			{
				u32 i = 0;

				std::wstringstream strm;

				do {
					i++;

					strm.clear();
					strm << name;
					strm << i;

				} while (m_items.find(strm.str()) != m_items.end());
				item->updateName(strm.str());
			}
			m_items[item->getName()] = item;
			item->registerObserver(this);
			return item;
		}
		return NULL;
    }

    template <class castTo>
    castTo* add2(castTo* item)
    {
        add(item);
        return item;
    }

    void remove(const std::wstring& name, bool freeAfter = true)
    {
        std::map<std::wstring, T* >::iterator it = m_items.find(name);
        if (it != m_items.end())
        {
			it->second->unregisterObserver(this);
            if (freeAfter)
            {
                delete (it->second);
            }
            m_items.erase(it);
        }
    }

	bool empty() const
	{
		return m_items.empty();
	}

    void clear(bool freeElements = true)
    {
        if (freeElements) 
        {
            std::map<std::wstring, T* >::iterator it;
            for (it = m_items.begin(); it != m_items.end(); ++it)
            {
                delete (it->second);
            }
        }
        m_items.clear();
    }

	bool OnEvent(const obs::sEvent& event)
	{
		switch (event.getID())
		{
			case obs::EVID_CHANGE_NAME:
			{
                CAST_EVENT(ev, sChangeNameEvent);
			
				T* obj = (*this)[ev.m_oldName];
				if (obj)
				{
					remove(ev.m_oldName, false);
					obj->updateName(ev.m_newName);
					add(obj);
				}
				break;
		
			}
		}
		return false;
	}
};

};

#endif